﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.ComponentModel.DataAnnotations;
using System.Security.Cryptography;
using Kelper.Domain.Models.Validation;

namespace Kelper.Domain.Models
{
    public class User
    {
        #region Secret Salt
        private const ulong Salt = 9517543383996018855;
        #endregion

        [Key]
        [Required(ErrorMessage = "Please specify a username")]
        [AlphaNumeric(ErrorMessage = "Please use only alfanumeric characters in the username")]
        [MaxLength(20, ErrorMessage = "The username may be at most 20 characters long")]
        public string Username
        {
            get { return username; }
            set { username = value == null ? null : value.Trim(); }
        }
        private string username;

        [Required(ErrorMessage = "Please specify an email address")]
        [MaxLength(40, ErrorMessage = "The email address may be at most 40 characters long")]
        [Email(ErrorMessage = "Please specify a valid email address")]
        public string Email
        {
            get { return email; }
            set { email = (value ?? "").ToLower(); }
        }
        private string email;

        [NotMapped]
        [Display(Name = "Confirm Email")]
        [Compare("Email", ErrorMessage = "The email addresses must match")]
        public string ConfirmEmail
        {
            get { return confirmEmail; }
            set { confirmEmail = (value ?? "").ToLower(); }
        }
        private string confirmEmail;

        /// <summary>
        /// The password is automatically hashed when assigning
        /// to the UnhashedPassword property, the resulting hash
        /// is stored in this property
        /// </summary>
        [Required]
        [StringLength(40)]
        public string HashedPassword { get; private set; }

        /// <summary>
        /// This method is a way to work with the password in an
        /// unhashed manner, to read the hashed password use the
        /// HashedPassword property
        /// </summary>
        [NotMapped]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        [Required(ErrorMessage = "Please specify a password")]
        public string UnhashedPassword
        {
            get { return unhashedPassword; }
            set
            {
                unhashedPassword = value;
                HashedPassword = Hash(value);
            }
        }
        private string unhashedPassword;

        [NotMapped]
        [DataType(DataType.Password)]
        [Display(Name = "Confirm Password")]
        [Compare("UnhashedPassword", ErrorMessage = "The passwords must match")]
        public string ConfirmPassword { get; set; }

        public virtual ICollection<Recipe> Recipes { get; set; }

        public bool PasswordEquals(string attempt)
        {
            if (UnhashedPassword != null)
                return Hash(attempt) == Hash(UnhashedPassword);
            else
                return Hash(attempt) == HashedPassword;
        }

        private string Hash(string str)
        {
            SHA1 sha1 = SHA1.Create();

            StringBuilder sb = new StringBuilder();
            foreach (byte b in sha1.ComputeHash(Encoding.UTF8.GetBytes(str + Salt)))
                sb.Append(b.ToString("X2"));
            return sb.ToString();
        }
    }
}